﻿using System;
using System.Collections.Generic;
using System.Threading;

namespace kemocard.Scripts.Common;

public class EventBus
{
    /**
     * 事件总线，订阅和分发事件
     */
    public EventBus()
    {
    }

    private readonly Dictionary<string, Action<object>> _eventDict = new();
    private readonly Dictionary<string, Action<object>> _tempEventDict = new();
    private readonly Dictionary<object, Dictionary<string, Action<object>>> _objDict = new();

    // 添加一个锁对象用于线程同步
    private readonly object _lockObject = new object();

    public void AddEvent(string eventName, Action<object> action)
    {
        lock (_lockObject)
        {
            if (_eventDict.TryAdd(eventName, action))
            {
                _eventDict[eventName] += action;
            }
        }
    }

    public void RemoveEvent(string eventName, Action<object> action)
    {
        lock (_lockObject)
        {
            if (!_eventDict.ContainsKey(eventName)) return;
            _eventDict[eventName] -= action;
            if (_eventDict[eventName] == null)
            {
                _tempEventDict.Remove(eventName);
            }
        }
    }

    public void PostEvent(string eventName, object arg = null)
    {
        // 创建事件处理器的副本以避免在执行过程中修改集合
        Action<object>[] eventHandlers = null;
        Action<object>[] objEventHandlers = null;

        // 锁定资源以安全地获取事件处理器列表
        lock (_lockObject)
        {
            if (_eventDict.TryGetValue(eventName, out var value) && value != null)
            {
                // 将多播委托转换为单独的委托数组
                var invocationList = value.GetInvocationList();
                eventHandlers = new Action<object>[invocationList.Length];
                for (int i = 0; i < invocationList.Length; i++)
                {
                    eventHandlers[i] = (Action<object>)invocationList[i];
                }
            }

            // 创建 objDict 的副本用于遍历
            var objDictCopy = new Dictionary<object, Dictionary<string, Action<object>>>(_objDict);

            // 收集对象事件处理器
            var objHandlers = new List<Action<object>>();
            foreach (var keyValuePair in objDictCopy)
            {
                if (keyValuePair.Value.TryGetValue(eventName, out Action<object> eventValue) && eventValue != null)
                {
                    var invocationList = eventValue.GetInvocationList();
                    foreach (var handler in invocationList)
                    {
                        objHandlers.Add((Action<object>)handler);
                    }
                }
            }

            if (objHandlers.Count > 0)
            {
                objEventHandlers = objHandlers.ToArray();
            }
        }

        // 在解锁后执行事件处理器，避免长时间持有锁
        if (eventHandlers != null)
        {
            foreach (var handler in eventHandlers)
            {
                try
                {
                    handler?.Invoke(arg);
                }
                catch (Exception ex)
                {
                    // 记录异常但继续执行其他处理器
                    Console.Error.WriteLine($"Error in event handler for {eventName}: {ex}");
                }
            }
        }

        if (objEventHandlers != null)
        {
            foreach (var handler in objEventHandlers)
            {
                try
                {
                    handler?.Invoke(arg);
                }
                catch (Exception ex)
                {
                    // 记录异常但继续执行其他处理器
                    Console.Error.WriteLine($"Error in object event handler for {eventName}: {ex}");
                }
            }
        }
    }

    public void AddEvent(object obj, string eventName, Action<object> action)
    {
        if (obj == null) return;

        lock (_lockObject)
        {
            if (_objDict.TryGetValue(obj, out var listener))
            {
                if (!listener.TryAdd(eventName, action))
                {
                    listener[eventName] += action;
                }
            }
            else
            {
                var tempDict = new Dictionary<string, Action<object>> { { eventName, action } };
                _objDict.Add(obj, tempDict);
            }
        }
    }

    public void RemoveEvent(object obj, string eventName, Action<object> action)
    {
        if (obj == null) return;

        lock (_lockObject)
        {
            if (!_objDict.TryGetValue(obj, out var listener)) return;
            if (!listener.ContainsKey(eventName)) return;
            listener[eventName] -= action;
            if (listener[eventName] != null) return;
            listener.Remove(eventName);
            if (_objDict[obj].Count == 0)
            {
                _objDict.Remove(obj);
            }
        }
    }

    public void RemoveObjAllEvents(object obj)
    {
        lock (_lockObject)
        {
            if (!_objDict.TryGetValue(obj, out var listener)) return;
            listener.Clear();
            _objDict.Remove(obj);
        }
    }

    public void PostEvent(object obj, string eventName, object arg = null)
    {
        Action<object> eventHandler = null;

        lock (_lockObject)
        {
            if (_objDict.TryGetValue(obj, out var listener))
            {
                listener.TryGetValue(eventName, out eventHandler);
            }
        }

        // 在解锁后执行事件处理器
        try
        {
            eventHandler?.Invoke(arg);
        }
        catch (Exception ex)
        {
            Console.Error.WriteLine($"Error in object event handler for {eventName}: {ex}");
        }
    }

    public void AddTempEvent(string eventName, Action<object> action)
    {
        lock (_lockObject)
        {
            _tempEventDict.TryAdd(eventName, action);
        }
    }

    public void RemoveTempEvent(string eventName, Action<object> action)
    {
        lock (_lockObject)
        {
            _tempEventDict.Remove(eventName);
        }
    }

    public void PostTempEvent(string eventName, object arg = null)
    {
        Action<object> eventHandler = null;

        lock (_lockObject)
        {
            _tempEventDict.TryGetValue(eventName, out eventHandler);
        }

        try
        {
            eventHandler?.Invoke(arg);
        }
        catch (Exception ex)
        {
            Console.Error.WriteLine($"Error in temp event handler for {eventName}: {ex}");
        }

        lock (_lockObject)
        {
            _tempEventDict.Remove(eventName);
        }
    }

    public void ClearByEventName(string eventName)
    {
        lock (_lockObject)
        {
            _eventDict.Remove(eventName);
            _tempEventDict.Remove(eventName);
        }
    }
}